#!/bin/bash
# Author: fanyongfeng
# 安装redis
# 操作系统：centos7/银河麒麟v10
set -e
# 使用root用户执行
[[ $(id -u) -ne 0 ]] && echo "请使用root用户执行此脚本" && exit 1

usage(){
    echo "Usage:"
    echo "sh $0
            --prefix=安装目录(默认/data/redis-\${version})
            --version=安装版本(默认7.0.15)
            --mode=single|cluster
            --ip_list=集群ip列表,用逗号分割
            --password=redis密码(单节点模式时可不提供，自动生成密码),集群模式密码需保持一致
        "
    echo "集群模式至少需要6个redis实例。如果仅提供3个IP，则会在每台服务器上部署2个redis实例"
    exit 1
}

#处理参数，规范化参数
ARGS=`getopt -o h --long prefix:,version:,mode:,ip_list:,password:,help -n "$0" -- "$@"`
if [[ $? != 0 || $# -eq 0 ]]
then
    usage
fi

#重新排列参数顺序
eval set -- "${ARGS}"
#通过shift和while循环处理参数
while true
do
    case $1 in
        --prefix)
            PREFIX=$2
            shift 2
            ;;
        --version)
            VERSION=$2
            shift 2
            ;;
        --mode)
            MODE=$2
            shift 2
            ;;
        --ip_list)
            IP_LIST=$2
            shift 2
            ;;
        --password)
            PASSWORD=$2
            shift 2
            ;;
        -h|-help)
            usage
            exit 1
            ;;
        --)
           shift
           break
           ;;
        *)
           echo "Internal error!"
           exit 1
           ;;
    esac
done

if [[ ${MODE} == cluster ]]
then
    if [[ -z ${IP_LIST} || -z ${PASSWORD} ]]
    then
        usage
    else
        IP_COUNTS=$(echo ${IP_LIST} | sed 's/,/\n/g' | wc -l)
        if [[ ${IP_COUNTS} -eq 3 ]]
        then
            INSTANCE_COUNTS=2
        else
            INSTANCE_COUNTS=1
        fi
    fi
fi

VERSION=${VERSION:-7.0.15}
INSTALL_DIR=${PREFIX:-/data/redis-${VERSION}}
CLUSTER_DIR="/data/redis-cluster"
RUN_USER="redis"
HOST_IP=$(ip a | grep global | head -1 | awk -F'/' '{print $1}' | awk '{print $2}')

if [[ "${MODE}" == "single" && -z "${PASSWORD}" ]]
then
    [[ -z ${PASSWORD} ]] && PASSWORD="$(gpg --gen-random --armor 1 12)"
fi

# 关闭防火墙和selinux
systemctl stop firewalld && systemctl disable firewalld
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce  0
# 安装依赖包
echo "安装依赖包..."
yum install -y gcc gcc-c++ make cmake tcl
# 下载redis
echo "下载redis..."
[[ -f redis-${VERSION}.tar.gz ]] || wget http://download.redis.io/releases/redis-${VERSION}.tar.gz
if [[ $? -ne 0 ]]
then
    echo "redis-${VERSION}.tar.gz下载失败，请重试或手动上传此安装包"
    exit 1
fi

# 编译redis
echo "编译redis..."
tar -zxf redis-${VERSION}.tar.gz && cd redis-${VERSION}
CPU_COUNTS=$(cat /proc/cpuinfo |grep processor | wc -l)
make -j${CPU_COUNTS} && make -j${CPU_COUNTS} test

if [ $? -eq 0 ]
then
    make PREFIX=${INSTALL_DIR} install
else
   echo "编译失败"
   exit 1
fi
# 添加环境变量
echo "export PATH=\$PATH:${INSTALL_DIR}/bin" > /etc/profile.d/redis.sh
source /etc/profile.d/redis.sh


mkdir -p ${INSTALL_DIR}/{data,logs}
if [[ ${INSTANCE_COUNTS} -eq 2 ]]
then
    mkdir -p ${CLUSTER_DIR}/{6379,6380}/{data,logs}
fi

# 添加redis用户并授权
useradd redis -s /sbin/nologin
chown -R redis.redis /data/redis*

# redis内存限制
# 使用available内存的50%分配JVM内存
FREE_MEM=$(free -g | sed -n '2p'| awk '{print int(($7 * 0.5))}')

if [[ "${FREE_MEM}" -eq 0 ]]; then
    MAXMEMORY=256mb
else
    MAXMEMORY=${FREE_MEM}gb
fi

# 修改系统参数
grep -q "vm.overcommit_memory" /etc/sysctl.conf || echo vm.overcommit_memory = 1 >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1

# 配置redis.conf
cat > ${INSTALL_DIR}/redis.conf <<EOF
##########################################基础参数配置############################################
bind ${HOST_IP}
protected-mode yes
#端口0代表完全禁用非TLS端口
port 6379
unixsocket ${INSTALL_DIR}/redis.sock
unixsocketperm 700
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile ${INSTALL_DIR}/redis.pid
loglevel notice
logfile ${INSTALL_DIR}/logs/redis.log
databases 16
always-show-logo yes
################################## 集群配置 ###################################
cluster-enabled yes
cluster-config-file "nodes.conf"
cluster-node-timeout 5000
################################## 安全认证 ###################################
##配置认证密码
requirepass ${PASSWORD}
masterauth ${PASSWORD}
################################# 持久化配置 #################################
#RDB 快照持久化
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ${INSTALL_DIR}/data
#AOF 持久化
appendonly no
appendfilename appendonly.aof
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-rewrite-incremental-fsync yes
################################## 连接配置 ##################################
maxclients 10000
################################## 内存管理 ###################################
maxmemory ${MAXMEMORY}
############################# 懒惰的释放 ####################################
lazyfree-lazy-eviction yes
lazyfree-lazy-expire yes
lazyfree-lazy-server-del yes
replica-lazy-flush yes
################################ LUA时间限制 ###############################
lua-time-limit 5000
############################### 慢日志 ################################
slowlog-log-slower-than 10000
slowlog-max-len 128
######################### 高级设置 #########################
activerehashing yes
#缓存空间限制
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 1024mb 256mb 300
client-output-buffer-limit pubsub 32mb 8mb 60
client-query-buffer-limit 1gb
#加快写入rdb 和aof
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
EOF

# 添加redis.service
echo "添加redis.service..."
cat > /etc/systemd/system/redis.service <<EOF
[Unit]
Description=redis service
Wants=network.target

[Service]
User=redis
Group=redis
Type=forking
ExecStart=${INSTALL_DIR}/bin/redis-server ${INSTALL_DIR}/redis.conf
ExecReload=/bin/kill -s HUP
ExecStop=/bin/kill -s QUIT
RestartSec=10
Restart=on-success

[Install]
WantedBy=multi-user.target
EOF


if [[ "${INSTANCE_COUNTS}" -eq 2 ]]
then
    \cp ${INSTALL_DIR}/redis.conf /data/redis-cluster/6379/redis.conf
    \cp ${INSTALL_DIR}/redis.conf /data/redis-cluster/6380/redis.conf
    sed -i "s#${INSTALL_DIR}#${CLUSTER_DIR}/6379#g" /data/redis-cluster/6379/redis.conf
    sed -i "s#${INSTALL_DIR}#${CLUSTER_DIR}/6380#g" /data/redis-cluster/6380/redis.conf
    sed -i "s/6379/6380/g" /data/redis-cluster/6380/redis.conf

    \cp /etc/systemd/system/redis.service /etc/systemd/system/redis-6379.service
    \cp /etc/systemd/system/redis.service /etc/systemd/system/redis-6380.service
    sed -i "s#${INSTALL_DIR}/redis.conf#${CLUSTER_DIR}/6379/redis.conf#g" /etc/systemd/system/redis-6379.service
    sed -i "s#${INSTALL_DIR}/redis.conf#${CLUSTER_DIR}/6380/redis.conf#g" /etc/systemd/system/redis-6380.service
    # 启动redis服务
    systemctl daemon-reload

    systemctl enable redis-6379
    systemctl enable redis-6380

    systemctl start redis-6379
    systemctl start redis-6380

    systemctl status redis-6379
    systemctl status redis-6380
elif [[ "${INSTANCE_COUNTS}" -eq 1 ]]
then
    systemctl daemon-reload
    systemctl enable redis
    systemctl start redis
fi

# 启动redis服务
echo "启动redis服务..."
if [[ "${MODE}" == "single" ]]
then
    sed -i "s/^masterauth/#masterauth/" ${INSTALL_DIR}/redis.conf
    sed -i "s/^cluster/#cluster/" ${INSTALL_DIR}/redis.conf
    systemctl daemon-reload
    systemctl enable redis
    systemctl start redis
else
    # 设置Redis Cluster集群
    echo "待所有redis实例启动后，设置redis集群模式，命令如下："
    if [[ "${INSTANCE_COUNTS}" -eq 2 ]]
    then
        IP_PORT_LIST_1=$(echo "${IP_LIST}"|sed 's/,/:6379 /g'|sed 's/$/:6379/g')
        IP_PORT_LIST_2=$(echo "${IP_LIST}"|sed 's/,/:6380 /g'|sed 's/$/:6380/g')
        IP_PORT_LIST="${IP_PORT_LIST_1} ${IP_PORT_LIST_2}"
        echo ${INSTALL_DIR}/bin/redis-cli -a ${PASSWORD} --cluster create --cluster-replicas 1 ${IP_PORT_LIST}
    else
        IP_PORT_LIST=$(echo ${IP_LIST}|sed 's/,/:6379 /g'|sed 's/$/:6379/g')
        echo ${INSTALL_DIR}/bin/redis-cli -a ${PASSWORD} --cluster create --cluster-replicas 1 ${IP_PORT_LIST}
    fi
fi

